#ifndef OPTFRAME_IILSPRVP_HPP_
#define OPTFRAME_IILSPRVP_HPP_

#include <math.h>
#include <vector>

#include "../../OptFrame/Heuristic/IntensifiedIteratedLocalSearch.hpp"
#include "../../OptFrame/Heuristic/BasicILSPerturbation.hpp"

typedef int BasicHistory;

class IILSPRVP: public IntensifiedIteratedLocalSearch<BasicHistory, RepPRVP, MemPRVP>
{
protected:
	Heuristic<RepPRVP, MemPRVP>& h;
	Heuristic<RepPRVP, MemPRVP>& h_best;
	BasicILSPerturbation<RepPRVP, MemPRVP>& p;
	Heuristic<RepPRVP, MemPRVP>& intens;
	int iterMax, bestIter, maxIter;
	string filename;

public:

	IILSPRVP(PRVPEvaluator& e, Heuristic<RepPRVP, MemPRVP>& _h, Heuristic<RepPRVP, MemPRVP>& _h_best, BasicILSPerturbation<RepPRVP, MemPRVP>& _p, Heuristic<RepPRVP, MemPRVP>& _intens, int _iterMax, string _filename) :
		IntensifiedIteratedLocalSearch<BasicHistory, RepPRVP, MemPRVP> (e), h(_h), h_best(_h_best), p(_p), intens(_intens), iterMax(_iterMax), filename(_filename)
	{
		bestIter = maxIter = 0;
	}

	virtual ~IILSPRVP()
	{
	}

	virtual BasicHistory& initializeHistory()
	{
		int& iter = *new int;
		iter = 0;

		return iter;
	}

	virtual void localSearch(SolutionPRVP& s, EvaluationPRVP& e, double timelimit, double target_f)
	{
		h.exec(s, e, timelimit, target_f);
	}

	virtual void perturbation(SolutionPRVP& s, EvaluationPRVP& e, double timelimit, double target_f, BasicHistory& history)
	{
		int iter = history;

		p.perturb(s, e, timelimit, target_f);

		// Incrementa a iteracao
		iter++;

		if (iter > maxIter)
			maxIter = iter;

		// Atualiza o historico
		history = iter;
	}

	virtual void intensification(SolutionPRVP& s, EvaluationPRVP& e, double timelimit, double target_f, BasicHistory& history)
	{

		//pegando iteração atual
		int iter = history;
		int intensification_rate = ceil(iterMax * 0.2);

		if (iter % intensification_rate == 0)
		{
			intens.exec(s, e, timelimit, target_f);
		}

	}

	virtual SolutionPRVP& acceptanceCriterion(const SolutionPRVP& s1, const SolutionPRVP& s2, BasicHistory& history)
	{
		if (IntensifiedIteratedLocalSearch<BasicHistory, RepPRVP, MemPRVP>::evaluator.betterThan(s2, s1))
		{
			// =======================
			//   Melhor solucao: 's2'
			// =======================
			EvaluationPRVP& e = IntensifiedIteratedLocalSearch<BasicHistory, RepPRVP, MemPRVP>::evaluator.evaluate(s2);
			cout << "Best fo: " << e.evaluation();
			cout << " on [iter " << history << "]" << endl;
			if (history > bestIter)
				bestIter = history;
			delete &e;

			// =======================
			//  Atualiza o historico
			// =======================
			// iter = 0
			history = 0;

			// =======================
			//    Retorna s2
			// =======================
			return s2.clone();
		}
		else
			return s1.clone();
	}

	virtual bool terminationCondition(BasicHistory& history)
	{
		int iter = history;

		return (iter >= iterMax);
	}

	void exec(SolutionPRVP& s, EvaluationPRVP& e, double timelimit, double target_f)
	{
		cout << "ILS exec(" << target_f << "," << timelimit << ")" << endl;

		Timer tnow;

		BasicHistory* history = &initializeHistory();

		// 's0' <- GenerateSolution
		// 's*' <- localSearch 's'


		localSearch(s, e, (timelimit - (tnow.now())), target_f);

		SolutionPRVP* sStar = &s.clone();
		EvaluationPRVP* eStar = &e.clone();

		cout << "ILS starts: ";
		e.print();

		do
		{
			SolutionPRVP* s1 = &sStar->clone();
			EvaluationPRVP* e1 = &eStar->clone();

			perturbation(*s1, *e1, (timelimit - (tnow.now())), target_f, *history);

			localSearch(*s1, *e1, (timelimit - (tnow.now())), target_f);

			intensification(*s1, *e1, (timelimit - (tnow.now())), target_f, *history);

			SolutionPRVP* s2 = s1;
			EvaluationPRVP* e2 = e1;

			SolutionPRVP* sStar1 = &acceptanceCriterion(*sStar, *s2, *history);

			delete sStar;
			delete eStar;
			delete s2;
			delete e2;

			sStar = sStar1;
			eStar = &evaluator.evaluate(*sStar);

		} while (evaluator.betterThan(target_f, eStar->evaluation()) && !terminationCondition(*history) && ((tnow.now()) < timelimit));

		SolutionPRVP* s2 = &sStar->clone();
		EvaluationPRVP* e2 = &eStar->clone();
		h_best.exec(*s2, *e2, timelimit, target_f);

		if (e2->evaluation() < eStar->evaluation())
		{
			cout << "Best fo: " << e2->evaluation();
			cout << " on BestImprovement" << endl;
			sStar = s2;
			eStar = e2;
		}

		//gravando no arquivo
		FILE* file = fopen(filename.c_str(), "a");
		if (!file)
		{
			cout << "Error creating file '" << filename << "'" << endl;
			return;
		}

		fprintf(file, "Maior Iteracao Melhora: %d \t Maior Iteracao sem Melhora: %d \n", bestIter, maxIter);
		fclose(file);

		e = *eStar;
		s = *sStar;

		delete eStar;
		delete sStar;
		delete s2;
		delete e2;

		delete history;
	}

};

#endif /*OPTFRAME_IILSPRVP_HPP_*/
